Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Displaying Raw • Download


meshchatx/src/backend/database/gifs.py b1ee794316b686bdda29b9b5dbb118afa201be4d (b1ee7943) Text, 8.87 KB

T8b949e# SPDX-License-Identifier: 0BSD

Tff7b72import T7ee787base64
Tff7b72import T7ee787sqlite3
Tff7b72import T7ee787time

Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backend Tff7b72import Te6edf3gif_utils


Tff7b72class T56d364UserGifsDAOTb4b4b4:
T8b949e"""Per-identity library of user-uploaded GIFs.

Mirrors UserStickersDAO but exposes a usage_count/last_used_at
pair so the picker can order entries by most-used and the user can quickly
reuse their favorite GIFs across chats.
"""

Tff7b72def Tff7b72__init__Tb4b4b4(Tff7b72selfTb4b4b4, Te6edf3providerTb4b4b4)Tb4b4b4:
Tff7b72selfTff7b72.Td2a8ffprovider Tff7b72= Te6edf3provider

Tff7b72def Td2a8ffcount_for_identityTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3identity_hashTb4b4b4: Tffa657strTb4b4b4) Tff7b72-Tff7b72> Tffa657intTb4b4b4:
Te6edf3row Tff7b72= Tff7b72selfTff7b72.Td2a8ffproviderTff7b72.Td2a8fffetchoneTb4b4b4(
Ta5d6ff"Ta5d6ffSELECT COUNT(*) AS c FROM user_gifs WHERE identity_hash = ?Ta5d6ff"Tb4b4b4,
Tb4b4b4(Te6edf3identity_hashTb4b4b4,Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72return Tffa657intTb4b4b4(Te6edf3rowTb4b4b4[Ta5d6ff"Ta5d6ffcTa5d6ff"Tb4b4b4]Tb4b4b4) Tff7b72if Te6edf3row Tff7b72else T79c0ff0

Tff7b72def Td2a8fflist_for_identityTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3identity_hashTb4b4b4: Tffa657strTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72selfTff7b72.Td2a8ffproviderTff7b72.Td2a8fffetchallTb4b4b4(
T8b949e"""
SELECT id, identity_hash, name, image_type, length(image_blob) AS image_size,
content_hash, source_message_hash, usage_count, last_used_at,
created_at, updated_at
FROM user_gifs
WHERE identity_hash = ?
ORDER BY usage_count DESC, last_used_at DESC, updated_at DESC, id DESC
"""Tb4b4b4,
Tb4b4b4(Te6edf3identity_hashTb4b4b4,Tb4b4b4)Tb4b4b4,
Tb4b4b4)

Tff7b72def Td2a8ffget_rowTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3gif_idTb4b4b4: Tffa657intTb4b4b4, Te6edf3identity_hashTb4b4b4: Tffa657strTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72selfTff7b72.Td2a8ffproviderTff7b72.Td2a8fffetchoneTb4b4b4(
T8b949e"""
SELECT id, identity_hash, name, image_type, image_blob, content_hash,
source_message_hash, usage_count, last_used_at,
created_at, updated_at
FROM user_gifs
WHERE id = ? AND identity_hash = ?
"""Tb4b4b4,
Tb4b4b4(Te6edf3gif_idTb4b4b4, Te6edf3identity_hashTb4b4b4)Tb4b4b4,
Tb4b4b4)

Tff7b72def Td2a8ffdeleteTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3gif_idTb4b4b4: Tffa657intTb4b4b4, Te6edf3identity_hashTb4b4b4: Tffa657strTb4b4b4) Tff7b72-Tff7b72> Tffa657boolTb4b4b4:
Te6edf3cur Tff7b72= Tff7b72selfTff7b72.Td2a8ffproviderTff7b72.Td2a8ffexecuteTb4b4b4(
Ta5d6ff"Ta5d6ffDELETE FROM user_gifs WHERE id = ? AND identity_hash = ?Ta5d6ff"Tb4b4b4,
Tb4b4b4(Te6edf3gif_idTb4b4b4, Te6edf3identity_hashTb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72return Te6edf3curTff7b72.Td2a8ffrowcount Tff7b72> T79c0ff0

Tff7b72def Td2a8ffdelete_all_for_identityTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3identity_hashTb4b4b4: Tffa657strTb4b4b4) Tff7b72-Tff7b72> Tffa657intTb4b4b4:
Te6edf3cur Tff7b72= Tff7b72selfTff7b72.Td2a8ffproviderTff7b72.Td2a8ffexecuteTb4b4b4(
Ta5d6ff"Ta5d6ffDELETE FROM user_gifs WHERE identity_hash = ?Ta5d6ff"Tb4b4b4,
Tb4b4b4(Te6edf3identity_hashTb4b4b4,Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72return Te6edf3curTff7b72.Td2a8ffrowcount

Tff7b72def Td2a8ffupdate_nameTb4b4b4(
Tff7b72selfTb4b4b4,
Te6edf3gif_idTb4b4b4: Tffa657intTb4b4b4,
Te6edf3identity_hashTb4b4b4: Tffa657strTb4b4b4,
Te6edf3nameTb4b4b4: Tffa657str Tff7b72| Tff7b72NoneTb4b4b4,
Tb4b4b4) Tff7b72-Tff7b72> Tffa657boolTb4b4b4:
Te6edf3now Tff7b72= Te6edf3timeTff7b72.Td2a8fftimeTb4b4b4(Tb4b4b4)
Te6edf3cur Tff7b72= Tff7b72selfTff7b72.Td2a8ffproviderTff7b72.Td2a8ffexecuteTb4b4b4(
T8b949e"""
UPDATE user_gifs
SET name = ?, updated_at = ?
WHERE id = ? AND identity_hash = ?
"""Tb4b4b4,
Tb4b4b4(Te6edf3nameTb4b4b4, Te6edf3nowTb4b4b4, Te6edf3gif_idTb4b4b4, Te6edf3identity_hashTb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72return Te6edf3curTff7b72.Td2a8ffrowcount Tff7b72> T79c0ff0

Tff7b72def Td2a8ffrecord_usageTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3gif_idTb4b4b4: Tffa657intTb4b4b4, Te6edf3identity_hashTb4b4b4: Tffa657strTb4b4b4) Tff7b72-Tff7b72> Tffa657boolTb4b4b4:
T8b949e"""Increment usage_count and refresh last_used_at for a GIF.

Returns True when a row was updated, False when the GIF does
not belong to the given identity.
"""
Te6edf3now Tff7b72= Te6edf3timeTff7b72.Td2a8fftimeTb4b4b4(Tb4b4b4)
Te6edf3cur Tff7b72= Tff7b72selfTff7b72.Td2a8ffproviderTff7b72.Td2a8ffexecuteTb4b4b4(
T8b949e"""
UPDATE user_gifs
SET usage_count = usage_count + 1, last_used_at = ?
WHERE id = ? AND identity_hash = ?
"""Tb4b4b4,
Tb4b4b4(Te6edf3nowTb4b4b4, Te6edf3gif_idTb4b4b4, Te6edf3identity_hashTb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72return Te6edf3curTff7b72.Td2a8ffrowcount Tff7b72> T79c0ff0

Tff7b72def Td2a8ffinsertTb4b4b4(
Tff7b72selfTb4b4b4,
Te6edf3identity_hashTb4b4b4: Tffa657strTb4b4b4,
Te6edf3nameTb4b4b4: Tffa657str Tff7b72| Tff7b72NoneTb4b4b4,
Te6edf3image_typeTb4b4b4: Tffa657strTb4b4b4,
Te6edf3image_bytesTb4b4b4: Tffa657bytesTb4b4b4,
Te6edf3source_message_hashTb4b4b4: Tffa657str Tff7b72| Tff7b72None Tff7b72= Tff7b72NoneTb4b4b4,
Tb4b4b4) Tff7b72-Tff7b72> Tffa657dict Tff7b72| Tff7b72NoneTb4b4b4:
T8b949e"""Insert a GIF. Returns summary dict or None if duplicate (same content_hash)."""
Tff7b72if Tff7b72selfTff7b72.Td2a8ffcount_for_identityTb4b4b4(Te6edf3identity_hashTb4b4b4) Tff7b72>Tff7b72= Te6edf3gif_utilsTff7b72.Td2a8ffMAX_GIFS_PER_IDENTITYTb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffgif_limit_reachedTa5d6ff"
Tff7b72raise Tf85149ValueErrorTb4b4b4(Te6edf3msgTb4b4b4)

Te6edf3ntTb4b4b4, Te6edf3ch Tff7b72= Te6edf3gif_utilsTff7b72.Td2a8ffvalidate_gif_payloadTb4b4b4(Te6edf3image_bytesTb4b4b4, Te6edf3image_typeTb4b4b4)
Te6edf3now Tff7b72= Te6edf3timeTff7b72.Td2a8fftimeTb4b4b4(Tb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3cur Tff7b72= Tff7b72selfTff7b72.Td2a8ffproviderTff7b72.Td2a8ffexecuteTb4b4b4(
T8b949e"""
INSERT INTO user_gifs (
identity_hash, name, image_type, image_blob, content_hash,
source_message_hash, usage_count, last_used_at, created_at, updated_at
)
VALUES (?, ?, ?, ?, ?, ?, 0, NULL, ?, ?)
"""Tb4b4b4,
Tb4b4b4(
Te6edf3identity_hashTb4b4b4,
Te6edf3nameTb4b4b4,
Te6edf3ntTb4b4b4,
Te6edf3image_bytesTb4b4b4,
Te6edf3chTb4b4b4,
Te6edf3source_message_hashTb4b4b4,
Te6edf3nowTb4b4b4,
Te6edf3nowTb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72except Te6edf3sqlite3Tff7b72.Td2a8ffIntegrityErrorTb4b4b4:
Tff7b72return Tff7b72None

Te6edf3new_id Tff7b72= Te6edf3curTff7b72.Td2a8fflastrowid
Te6edf3row Tff7b72= Tff7b72selfTff7b72.Td2a8ffproviderTff7b72.Td2a8fffetchoneTb4b4b4(
T8b949e"""
SELECT id, identity_hash, name, image_type, length(image_blob) AS image_size,
content_hash, source_message_hash, usage_count, last_used_at,
created_at, updated_at
FROM user_gifs
WHERE id = ?
"""Tb4b4b4,
Tb4b4b4(Te6edf3new_idTb4b4b4,Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72return Tffa657dictTb4b4b4(Te6edf3rowTb4b4b4) Tff7b72if Te6edf3row Tff7b72else Tff7b72None

Tff7b72def Td2a8ffexport_payloads_for_identityTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3identity_hashTb4b4b4: Tffa657strTb4b4b4) Tff7b72-Tff7b72> Tffa657listTb4b4b4[Tffa657dictTb4b4b4]Tb4b4b4:
Te6edf3rows Tff7b72= Tff7b72selfTff7b72.Td2a8ffproviderTff7b72.Td2a8fffetchallTb4b4b4(
T8b949e"""
SELECT name, image_type, image_blob, source_message_hash, usage_count
FROM user_gifs
WHERE identity_hash = ?
ORDER BY id ASC
"""Tb4b4b4,
Tb4b4b4(Te6edf3identity_hashTb4b4b4,Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3out Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72for Te6edf3r Tff7b72in Te6edf3rowsTb4b4b4:
Te6edf3blob Tff7b72= Te6edf3rTb4b4b4[Ta5d6ff"Ta5d6ffimage_blobTa5d6ff"Tb4b4b4]
Te6edf3b64 Tff7b72= Te6edf3base64Tff7b72.Td2a8ffb64encodeTb4b4b4(Te6edf3blobTb4b4b4)Tff7b72.Td2a8ffdecodeTb4b4b4(Ta5d6ff"Ta5d6ffasciiTa5d6ff"Tb4b4b4)
Te6edf3outTff7b72.Td2a8ffappendTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffnameTa5d6ff"Tb4b4b4: Te6edf3rTb4b4b4[Ta5d6ff"Ta5d6ffnameTa5d6ff"Tb4b4b4]Tb4b4b4,
Ta5d6ff"Ta5d6ffimage_typeTa5d6ff"Tb4b4b4: Te6edf3rTb4b4b4[Ta5d6ff"Ta5d6ffimage_typeTa5d6ff"Tb4b4b4]Tb4b4b4,
Ta5d6ff"Ta5d6ffimage_bytesTa5d6ff"Tb4b4b4: Te6edf3b64Tb4b4b4,
Ta5d6ff"Ta5d6ffsource_message_hashTa5d6ff"Tb4b4b4: Te6edf3rTb4b4b4[Ta5d6ff"Ta5d6ffsource_message_hashTa5d6ff"Tb4b4b4]Tb4b4b4,
Ta5d6ff"Ta5d6ffusage_countTa5d6ff"Tb4b4b4: Tffa657intTb4b4b4(Te6edf3rTb4b4b4[Ta5d6ff"Ta5d6ffusage_countTa5d6ff"Tb4b4b4] Tff7b72or T79c0ff0Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tff7b72return Te6edf3out

Tff7b72def Td2a8ffimport_payloadsTb4b4b4(
Tff7b72selfTb4b4b4,
Te6edf3identity_hashTb4b4b4: Tffa657strTb4b4b4,
Te6edf3itemsTb4b4b4: Tffa657listTb4b4b4[Tffa657dictTb4b4b4]Tb4b4b4,
Tff7b72*Tb4b4b4,
Te6edf3replace_duplicatesTb4b4b4: Tffa657boolTb4b4b4,
Tb4b4b4) Tff7b72-Tff7b72> Tffa657dictTb4b4b4:
Te6edf3imported Tff7b72= T79c0ff0
Te6edf3skipped_duplicates Tff7b72= T79c0ff0
Te6edf3skipped_invalid Tff7b72= T79c0ff0
Te6edf3errorsTb4b4b4: Tffa657listTb4b4b4[Tffa657strTb4b4b4] Tff7b72= Tb4b4b4[Tb4b4b4]

Tff7b72for Te6edf3iTb4b4b4, Te6edf3item Tff7b72in Tffa657enumerateTb4b4b4(Te6edf3itemsTb4b4b4)Tb4b4b4:
Te6edf3name Tff7b72= Te6edf3gif_utilsTff7b72.Td2a8ffsanitize_gif_nameTb4b4b4(Te6edf3itemTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffnameTa5d6ff"Tb4b4b4)Tb4b4b4)
Te6edf3it Tff7b72= Te6edf3itemTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffimage_typeTa5d6ff"Tb4b4b4)
Te6edf3b64 Tff7b72= Te6edf3itemTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffimage_bytes_b64Ta5d6ff"Tb4b4b4)
Te6edf3src Tff7b72= Te6edf3itemTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffsource_message_hashTa5d6ff"Tb4b4b4)
Te6edf3usage Tff7b72= Tffa657intTb4b4b4(Te6edf3itemTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffusage_countTa5d6ff"Tb4b4b4) Tff7b72or T79c0ff0Tb4b4b4)
Te6edf3usage Tff7b72= Tffa657maxTb4b4b4(Te6edf3usageTb4b4b4, T79c0ff0Tb4b4b4)
Tff7b72tryTb4b4b4:
Tff7b72if Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3b64Tb4b4b4, Tb4b4b4(Tffa657strTb4b4b4, Tffa657bytesTb4b4b4)Tb4b4b4) Tff7b72or Tff7b72not Te6edf3b64Tb4b4b4:
Te6edf3skipped_invalid Tff7b72+Tff7b72= T79c0ff1
Te6edf3errorsTff7b72.Td2a8ffappendTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffdecode_failed_at_Tffd700{Te6edf3iTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72continue
Te6edf3raw Tff7b72= Te6edf3base64Tff7b72.Td2a8ffb64decodeTb4b4b4(Te6edf3b64Tb4b4b4, Te6edf3validateTff7b72=Tff7b72FalseTb4b4b4)
Tff7b72except Tb4b4b4(Tf85149ValueErrorTb4b4b4, Tf85149TypeErrorTb4b4b4)Tb4b4b4:
Te6edf3skipped_invalid Tff7b72+Tff7b72= T79c0ff1
Te6edf3errorsTff7b72.Td2a8ffappendTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffdecode_failed_at_Tffd700{Te6edf3iTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72continue
Tff7b72tryTb4b4b4:
Te6edf3ntTb4b4b4, Te6edf3ch Tff7b72= Te6edf3gif_utilsTff7b72.Td2a8ffvalidate_gif_payloadTb4b4b4(Te6edf3rawTb4b4b4, Te6edf3itTb4b4b4)
Tff7b72except Tf85149ValueErrorTb4b4b4:
Te6edf3skipped_invalid Tff7b72+Tff7b72= T79c0ff1
Te6edf3errorsTff7b72.Td2a8ffappendTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6ffinvalid_payload_at_Tffd700{Te6edf3iTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72continue

Te6edf3existing Tff7b72= Tff7b72selfTff7b72.Td2a8ffproviderTff7b72.Td2a8fffetchoneTb4b4b4(
Ta5d6ff"Ta5d6ffSELECT id FROM user_gifs WHERE identity_hash = ? AND content_hash = ?Ta5d6ff"Tb4b4b4,
Tb4b4b4(Te6edf3identity_hashTb4b4b4, Te6edf3chTb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3existingTb4b4b4:
Tff7b72if Tff7b72not Te6edf3replace_duplicatesTb4b4b4:
Te6edf3skipped_duplicates Tff7b72+Tff7b72= T79c0ff1
Tff7b72continue
Tff7b72selfTff7b72.Td2a8ffproviderTff7b72.Td2a8ffexecuteTb4b4b4(
Ta5d6ff"Ta5d6ffDELETE FROM user_gifs WHERE identity_hash = ? AND content_hash = ?Ta5d6ff"Tb4b4b4,
Tb4b4b4(Te6edf3identity_hashTb4b4b4, Te6edf3chTb4b4b4)Tb4b4b4,
Tb4b4b4)

Tff7b72if Tb4b4b4(
Tff7b72selfTff7b72.Td2a8ffcount_for_identityTb4b4b4(Te6edf3identity_hashTb4b4b4)
Tff7b72>Tff7b72= Te6edf3gif_utilsTff7b72.Td2a8ffMAX_GIFS_PER_IDENTITY
Tb4b4b4)Tb4b4b4:
Te6edf3errorsTff7b72.Td2a8ffappendTb4b4b4(Ta5d6ff"Ta5d6ffgif_limit_reachedTa5d6ff"Tb4b4b4)
Tff7b72break

Te6edf3now Tff7b72= Te6edf3timeTff7b72.Td2a8fftimeTb4b4b4(Tb4b4b4)
Tff7b72tryTb4b4b4:
Tff7b72selfTff7b72.Td2a8ffproviderTff7b72.Td2a8ffexecuteTb4b4b4(
T8b949e"""
INSERT INTO user_gifs (
identity_hash, name, image_type, image_blob, content_hash,
source_message_hash, usage_count, last_used_at, created_at, updated_at
)
VALUES (?, ?, ?, ?, ?, ?, ?, NULL, ?, ?)
"""Tb4b4b4,
Tb4b4b4(
Te6edf3identity_hashTb4b4b4,
Te6edf3nameTb4b4b4,
Te6edf3ntTb4b4b4,
Te6edf3rawTb4b4b4,
Te6edf3chTb4b4b4,
Te6edf3src Tff7b72if Tffa657isinstanceTb4b4b4(Te6edf3srcTb4b4b4, Tffa657strTb4b4b4) Tff7b72else Tff7b72NoneTb4b4b4,
Te6edf3usageTb4b4b4,
Te6edf3nowTb4b4b4,
Te6edf3nowTb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3imported Tff7b72+Tff7b72= T79c0ff1
Tff7b72except Te6edf3sqlite3Tff7b72.Td2a8ffIntegrityErrorTb4b4b4:
Te6edf3skipped_duplicates Tff7b72+Tff7b72= T79c0ff1

Tff7b72return Tb4b4b4{
Ta5d6ff"Ta5d6ffimportedTa5d6ff"Tb4b4b4: Te6edf3importedTb4b4b4,
Ta5d6ff"Ta5d6ffskipped_duplicatesTa5d6ff"Tb4b4b4: Te6edf3skipped_duplicatesTb4b4b4,
Ta5d6ff"Ta5d6ffskipped_invalidTa5d6ff"Tb4b4b4: Te6edf3skipped_invalidTb4b4b4,
Ta5d6ff"Ta5d6fferrorsTa5d6ff"Tb4b4b4: Te6edf3errorsTb4b4b4,
Tb4b4b4}


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────